﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using OpenTK;

using OpenTK.Graphics.OpenGL;

using Xeno.Module;
using OpenTK;
namespace Xeno.Core
{

    public class XenoImport
    {
        private static aiPostProcessSteps ppsteps =
          aiPostProcessSteps.aiProcess_CalcTangentSpace | // calculate tangents and bitangents if possible
          aiPostProcessSteps.aiProcess_JoinIdenticalVertices | // join identical vertices/ optimize indexing
          aiPostProcessSteps.aiProcess_ValidateDataStructure | // perform a full validation of the loader's output
          aiPostProcessSteps.aiProcess_ImproveCacheLocality | // improve the cache locality of the output vertices
          aiPostProcessSteps.aiProcess_RemoveRedundantMaterials | // remove redundant materials
          aiPostProcessSteps.aiProcess_FindDegenerates | // remove degenerated polygons from the import
          aiPostProcessSteps.aiProcess_FindInvalidData | // detect invalid model data, such as invalid normal vectors
          aiPostProcessSteps.aiProcess_GenUVCoords | // convert spherical, cylindrical, box and planar mapping to proper UVs
          aiPostProcessSteps.aiProcess_TransformUVCoords | // preprocess UV transformations (scaling, translation ...)
          aiPostProcessSteps.aiProcess_FindInstances | // search for instanced meshes and remove them by references to one master
          aiPostProcessSteps.aiProcess_LimitBoneWeights | // limit bone weights to 4 per vertex
          aiPostProcessSteps.aiProcess_OptimizeMeshes | // join small meshes, if possible;
          (aiPostProcessSteps)0;
        private static List<XenoMaterial> TmpMatList = new List<XenoMaterial>();
        private static bool aiInit = false;
        public static XenoNode ImportAI(string path,string name,XenoNode parent,float importScale)
        {
   
            XenoNode root = XenoFactory.Node.PrepNode(name, parent);
            var flags = (ppsteps |
          aiPostProcessSteps.aiProcess_GenSmoothNormals | // generate smooth normal vectors if not existing
          aiPostProcessSteps.aiProcess_SplitLargeMeshes | // split large, unrenderable meshes into submeshes
          aiPostProcessSteps.aiProcess_Triangulate | // triangulate polygons with more than 3 edges
          aiPostProcessSteps.aiProcess_ConvertToLeftHanded | // convert everything to D3D left handed space
          aiPostProcessSteps.aiProcess_SortByPType | // make 'clean' meshes which consist of a single typ of primitives
          (aiPostProcessSteps)0);

            var imp = new Importer();

            var scene = imp.ReadFile(path, flags);
            if (scene != null)
            {
                if (scene.HasMaterials())
                {
                    TmpMatList.Clear();
                    foreach (var m in scene.mMaterials)
                    {
                        var mat = new XenoMaterial(path + "_Mat" + TmpMatList.Count.ToString());
                        ProcessMaterialAI(mat, m);
                        TmpMatList.Add(mat);
                    }
                }
                foreach (var m in scene.mMeshes)
                {
                    var mn = XenoFactory.Node.Mesh(name, root);
                    ProcessMeshAI(m, mn);
                }
            }

            return root;
        }
        public static void ProcessMaterialAI(XenoMaterial xm, aiMaterial m)
        {
            aiString path = new aiString();


            if (m.GetTextureDiffuse0(path))
            {
                xm.DiffuseMap = new XenoTexture2D(path.Data);

            }
            //aiVector3D 

            aiColor4D col = new aiColor4D();
            if (m.GetDiffuse(col))
            {
                xm.DiffuseColor = new OpenTK.Vector4(col.r, col.g, col.b, col.a);
            }
            else
            {

                xm.DiffuseColor = new OpenTK.Vector4(1, 1, 1, 1);
            }
        }

            //xm.DiffuseColor =
        public static void ProcessMeshAI(aiMesh m, XenoNode n)
        {
            bool genNormals = false;
            XenoVertex[] vl = new XenoVertex[m.mNumVertices];
            if (m.HasPositions())
            {

                int vi = 0;
                foreach (var v in m.mVertices)
                {
                    vl[vi] = new XenoVertex(new OpenTK.Vector3(v.x, v.y, v.z));
                    vi++;
                }
            }
            else
            {

                return;
            }
            if (m.HasNormals())
            {
                int vi = 0;
                foreach (var an in m.mNormals)
                {
                    vl[vi].Normal = new OpenTK.Vector3(an.x, an.y, an.z);
                    vi++;
                }
            }
            else
            {
                genNormals = true;
            }
            if (m.HasTangentsAndBitangents())
            {
                int vi = 0;
                foreach (var b in m.mBitangents)
                {
                    vl[vi].BiNormal = new OpenTK.Vector3(b.x, b.y, b.z);
                    vi++;
                }
                vi = 0;
                foreach (var t in m.mTangents)
                {
                    vl[vi].Tangent = new OpenTK.Vector3(t.x, t.y, t.z);
                    vi++;
                }
            }
            else
            {
                genNormals = true;
            }
            if (m.HasTextureCoords(0))
            {
                int vi = 0;
                foreach (var t in m.mTextureCoords)
                {
                    if (t.Count > 0)
                    {
                        vl[vi].Tex0 = new OpenTK.Vector3(t[0].x, t[0].y, t[0].z);
                        vi++;
                    }
                }
            }
            XenoTriangle[] tl = new XenoTriangle[m.mNumFaces];
            if (m.HasFaces())
            {
                int ti =0;
                foreach (var f in m.mFaces)
                {
                    if (f.mIndices.Count >= 3)
                    {
                        tl[ti] = new XenoTriangle((int)f.mIndices[0], (int)f.mIndices[1], (int)f.mIndices[2]);
                        ti++;
                    }
                    //ti++;

                }
            }
            n.Mesh.AddVertices = vl;
            n.Mesh.AddTriangles = tl;
            if (m.mMaterialIndex >= 0 && m.mMaterialIndex < TmpMatList.Count)
            {
                n.MeshRenderer.Material = TmpMatList[(int)m.mMaterialIndex];
            }
            else
            {

                n.MeshRenderer.Material = new XenoMaterial("Tmp");
            }

        }
        public static XenoNode ImportObj(string path, string name,XenoNode parent,Vector3 importScale)
        {

            XenoNode root = new XenoNode(name, parent);
            //XenoMesh mesh = root.Mesh;
            XenoNode aNode = null;
            XenoMesh aMesh = null;

            

            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            TextReader tr = new StreamReader(fs);

            string[] deli = new string[4];
            deli[0] = " ";
            deli[1] = "\n";
            deli[2] = "\r";
            deli[3] = "\nv";

            List<Vector3> Vertices = new List<Vector3>();
            List<Vector3> Normals = new List<Vector3>();
            List<Vector3> Tex = new List<Vector3>();
            int nIndex=0;
            int tIndex = 0;

            string[] deli2 = new string[3];
            deli2[0] = "/";
            deli2[1] = "\n";
            deli2[2] = "\r";
            bool firstVert = true;
            int NodeIndex = 0;
            while (fs.Position < fs.Length)
            {

                string code = tr.ReadToEnd();

                string[] toks = code.Split(deli, StringSplitOptions.RemoveEmptyEntries);



                for(int i=0;i<toks.Length;i++)
                {
                    string t = toks[i];
                    bool skip = false;
                    switch (t.ToLower())
                    {
                        case "mtllib":
                            
                            break;
                        case "v":
                            if (firstVert)
                            {
                                if (aMesh != null)
                                {
                                    aMesh.UpdateNormals();
                                    aNode.MeshRenderer.Sync = true;
                                }
                                aNode = XenoFactory.Node.Mesh(name + "_" + NodeIndex, root);
                                aMesh = aNode.Mesh;
                                firstVert = false;
                             
                                NodeIndex++;
                            }
                            Vector3 vec = GetVec3(toks, i);

                            vec.Scale(ref importScale);
                        
                            Vertices.Add(vec);

                        

                           

                            break;
                        case "vn":

                            Vector3 vec2 = GetVec3(toks, i);

                            Normals.Add(vec2);
                           
                            

                            break;
                        case "vt":

                            
                            float u = float.Parse(toks[i + 1]);
                            float v = float.Parse(toks[i + 2]);

                            Tex.Add(new Vector3(u, v, 0));

                         

                            break;

                        case "f":

                         
                            firstVert = true;
                            int cC = 0;
                            string[] cCod = new string[255];

                            for (int k = i+1; k < toks.Length; k++)
                            {
                                bool skip3 = false;
                                string code2 = toks[k];
                                switch (code2.ToLower())
                                {
                                    case "v":
                                    case "f":
                                    case "vn":
                                    case "vt":
                                        skip3 = true;
                                        break;

                                }
                                if(skip3) break;
                                cCod[cC] = code2;
                                cC++;
                            }

                            if (cC == 3)
                            {
                                int index3=0;
                                  int iVert, iNorm, iTex;
                                iVert = iNorm = iTex = 0;
                                if (iNorm < 1)
                                {
                                    iNorm = 1;
                                }
                                Vector3 v1, v2, v3;
                                Vector3 n1, n2, n3;
                                Vector3 t1, t2, t3;

                                n1 = n2 = n3 = Vector3.Zero;
                                t1 = t2 = t3 = Vector3.Zero;
                                ProcComp(cCod,0, ref iVert, ref iNorm, ref iTex);

                                v1 = Vertices[iVert-1];
                                if (Normals.Count > 0)
                                {

                                    n1 = Normals[iNorm - 1];
                                }
                                if (Tex.Count > 0)
                                {
                                    t1 = Tex[iTex - 1];
                                }

                                ProcComp(cCod, 1, ref iVert, ref iNorm, ref iTex);

                                v2 = Vertices[iVert-1];
                                if (Normals.Count > 0)
                                {
                                    n2 = Normals[iNorm - 1];
                                }
                                if (Tex.Count > 0)
                                {
                                    t2 = Tex[iTex - 1];
                                }
                                ProcComp(cCod, 2, ref iVert, ref iNorm, ref iTex);

                                v3 = Vertices[iVert-1];
                                if (Normals.Count > 0)
                                {
                                    n3 = Normals[iNorm - 1];
                                }
                                if (Tex.Count > 0)
                                {
                                    t3 = Tex[iTex - 1];
                                }

                                int vIndex = aMesh.Vertices.Count;

                                aMesh.AddVertex = new XenoVertex(v1, n1, t1);
                                aMesh.AddVertex = new XenoVertex(v2, n2, t2);
                                aMesh.AddVertex = new XenoVertex(v3, n3, t3);

                                aMesh.AddTri(vIndex, vIndex + 1, vIndex + 2);


                            }
                            else if (cC == 4)
                            {
                                int index = 0;
                                int iVert, iNorm, iTex;
                                iVert = iNorm = iTex = 0;
                                if (iNorm < 1)
                                {
                                    iNorm = 1;
                                }
                                Vector3 v1, v2, v3,v4;
                                Vector3 n1, n2, n3,n4;
                                Vector3 t1, t2, t3,t4;

                                n1 = n2 = n3 = n4 = Vector3.Zero;
                                t1 = t2 = t3 = t4 =  Vector3.Zero;
                                ProcComp(cCod, 0, ref iVert, ref iNorm, ref iTex);

                                v1 = Vertices[iVert - 1];
                                if (Normals.Count > 0)
                                {

                                    n1 = Normals[iNorm - 1];
                                }
                                if (Tex.Count > 0)
                                {
                                    t1 = Tex[iTex - 1];
                                }

                                ProcComp(cCod, 1, ref iVert, ref iNorm, ref iTex);

                                v2 = Vertices[iVert - 1];
                                if (Normals.Count > 0)
                                {
                                    n2 = Normals[iNorm - 1];
                                }
                                if (Tex.Count > 0)
                                {
                                    t2 = Tex[iTex - 1];
                                }
                                ProcComp(cCod, 2, ref iVert, ref iNorm, ref iTex);

                                v3 = Vertices[iVert - 1];
                                if (Normals.Count > 0)
                                {
                                    n3 = Normals[iNorm - 1];
                                }
                                if (Tex.Count > 0)
                                {
                                    t3 = Tex[iTex - 1];
                                }

                                ProcComp(cCod, 3, ref iVert, ref iNorm, ref iTex);

                                v4 = Vertices[iVert - 1];
                                if (Normals.Count > 0)
                                {
                                    n4 = Normals[iNorm - 1];
                                }
                                if (Tex.Count > 0)
                                {
                                    t4 = Tex[iTex - 1];
                                }


                                
                                
                                int vIndex = aMesh.Vertices.Count;

                                aMesh.AddVertex = new XenoVertex(v1, n1, t1);
                                aMesh.AddVertex = new XenoVertex(v2, n2, t2);
                                aMesh.AddVertex = new XenoVertex(v3, n3, t3);
                                aMesh.AddVertex = new XenoVertex(v4, n4, t4);

                                aMesh.AddTri(vIndex, vIndex + 1, vIndex + 2);
                                aMesh.AddTri(vIndex + 2, vIndex + 3, vIndex);
                                aMesh.AddTri(vIndex, vIndex + 2, vIndex + 1);
                                aMesh.AddTri(vIndex + 2, vIndex, vIndex + 3);
                            }

                            break;
                        default:
                      
                            break;
                    }
                    if (skip) break;
                }



            }

            fs.Close();

            tr = null;
            fs = null;

            aMesh.UpdateNormals();
            aNode.MeshRenderer.Sync = true;

            return root;

        }

        private static void ProcComp(string[] cCod, int index3, ref int iVert, ref int iNorm, ref int iTex)
        {
            string[] deli3 = new string[1];
            deli3[0] = "/";
            string[] cInd = cCod[index3].Split(deli3,StringSplitOptions.RemoveEmptyEntries);


            switch (cInd.Length)
            {
                case 1:
                    iVert = int.Parse(cInd[0]);
                    break;
                case 2:
                    iVert = int.Parse(cInd[0]);
                    iTex = int.Parse(cInd[1]);
                    break;
                case 3:
                    iVert = int.Parse(cInd[0]);
                    if (cInd[1] == "/")
                    {
                        iNorm = int.Parse(cInd[2]);
                    }
                    else
                    {
                        iTex = int.Parse(cInd[1]);
                        iNorm = int.Parse(cInd[2]);
                    }
                    break;


            }
        }

        private static void ProcessSeg(string[] deli2, string seg1, int ci, int[,] co)
        {

            string[] par = seg1.Split(deli2, StringSplitOptions.RemoveEmptyEntries);

            int pos = int.Parse(par[0]);
            int tex = int.Parse(par[1]);
            int norm = int.Parse(par[2]);
            co[ci, 0] = pos;
            co[ci, 1] = tex;
            co[ci, 2] = norm;
        }

     
        private static Vector3 GetVec3(string[] toks, int i)
        {
            float x = float.Parse(toks[i + 1]);
            float y = float.Parse(toks[i + 2]);
            float z = float.Parse(toks[i + 3]);
            Vector3 vec = new Vector3(x, y, z);
            return vec;
        }

    }

}
